package org.github.manx98.jlibhac.common.math;

import cn.hutool.core.util.ByteUtil;

import java.math.BigInteger;
import java.nio.ByteOrder;

/**
 * Uint32
 *
 * @author Manx98
 * @version 1.0
 * @since 2022/10/2 20:37
 */
public class Uint64 extends UnsignedNumber {
    private final boolean signum;
    private final long value;

    public Uint64(byte[] val) {
        this(val, 0);
    }

    public Uint64(byte[] val, int off) {
        this(val, off, ByteOrder.LITTLE_ENDIAN);
    }

    public Uint64(byte[] magnitude, int off, ByteOrder order) {
        this(ByteUtil.bytesToLong(magnitude, off, order));
    }

    public Uint64(long value) {
        this.signum = value < 0;
        this.value = value & Long.MAX_VALUE;
    }

    @Override
    public UnsignedNumber shiftLeft(int n) {
        return new Uint64(value << n);
    }

    @Override
    public UnsignedNumber shiftRight(int n) {
        if (signum) {
            if (n <= 63) {
                return new Uint64((value >> n) | (1L << (63 - n)));
            }
            return Uint16.ZERO;
        }
        return new Uint64(value >> n);
    }

    @Override
    public UnsignedNumber and(UnsignedNumber val) {
        return new Uint64(longValue() & val.longValue());
    }

    @Override
    public UnsignedNumber or(UnsignedNumber val) {
        return new Uint64(longValue() | val.longValue());
    }

    @Override
    public UnsignedNumber xor(UnsignedNumber val) {
        return new Uint64(longValue() ^ val.longValue());
    }

    @Override
    public UnsignedNumber add(UnsignedNumber val) {
        return new Uint64(longValue() + val.longValue());
    }

    @Override
    public byte[] toByteArray(ByteOrder order) {
        byte[] bytes = new byte[8];
        toByteArray(bytes, 0, order);
        return bytes;
    }

    @Override
    public void toByteArray(byte[] result, int offset, ByteOrder byteOrder) {
        long longValue = this.longValue();
        if (ByteOrder.LITTLE_ENDIAN == byteOrder) {
            for (int i = 0; i < 8; ++i) {
                result[offset + i] = (byte) ((int) (longValue & 255L));
                longValue >>= 8;
            }
        } else {
            for (int i = 7; i >= 0; --i) {
                result[offset + i] = (byte) ((int) (longValue & 255L));
                longValue >>= 8;
            }
        }
    }

    @Override
    public int intValue() {
        return (int) longValue();
    }

    @Override
    public long longValue() {
        if (signum) {
            return value | Long.MIN_VALUE;
        }
        return value;
    }

    @Override
    public float floatValue() {
        return longValue();
    }

    @Override
    public double doubleValue() {
        return longValue();
    }

    @Override
    public String toString() {
        if (signum) {
            return toString(10);
        } else {
            return String.valueOf(value);
        }
    }

    public String toString(int radix) {
        if (signum) {
            return BigInteger.valueOf(value).or(BigInteger.ONE.shiftLeft(63)).toString();
        } else {
            return BigInteger.valueOf(value).toString(radix);
        }
    }
}
